#include <map>
#include "tagfs_map.h"
#include "tagfs_file.h"
#include "tools.h"

#define DEBUG_ON

// --------------------------------
void TagFS_Map::init(TagFS_Node* n)
// --------------------------------
{
	nodes = n;
	nodes->reserve(0);
	read(&mapMap);
	#ifdef DEBUG_ON
	printf("[DEBUG] filemaps: %u\n", mapMap.file.size());
	printf("[DEBUG] tagmaps: %u\n", mapMap.tag.size());
	#endif
	fileMapSize = tagMapSize = 0;
	if (!mapMap.file.empty())
	{
		mapMap.curFile = 0;
		read(mapMap.file[0], &fileMap);
		reserveFileNodes(&fileMap);
		fileMapSize = fileMapSizeCalc(&fileMap);
	}
	if (!mapMap.tag.empty())
	{
		mapMap.curTag = 0;
		read(mapMap.tag[0], &tagMap);
		tagMapSize = tagMapSizeCalc(&tagMap);
	}
	#ifdef DEBUG_ON
	printf("[DEBUG] current filemap size: %u\n", fileMapSize);
	printf("[DEBUG] current tagmap size: %u\n", tagMapSize);
	#endif
}

// ------------------------------
void TagFS_Map::read(MapMap *map)
// ------------------------------
{
	char data[nodes->getSize()];
	std::pair<uint64_t, int> res;
	res.first = 0;

	uint8_t zerosHit = 0;
	uint64_t number = 0;

	while (zerosHit < 2)
	{
		res = nodes->read(res.first, data);
		for (uint16_t i=0; i<nodes->getSize(); i+=8)
		{
			number = *((uint64_t *)(&data[i]));
			#ifdef DEBUG_ON
			printf("[DEBUG] read %llu\n", number);
			#endif
			if (number == 0)
			{
				zerosHit++;
				if (zerosHit == 2)
				{
					break;
				}
			}
			else
			{
				if (zerosHit == 0)
				{
					map->file.push_back(number);
					nodes->reserve(number);
				}
				else if (zerosHit == 1)
				{
					map->tag.push_back(number);				
					nodes->reserve(number);
				}
			}
		}
		if (res.first == 0 || res.second != 0)
		{
			break;
		}
	}
}

// -------------------------------
void TagFS_Map::write(MapMap *map)
// -------------------------------
{
	char data[nodes->getSize()];
	uint16_t wrote = 0;
	std::pair<uint64_t, int> res;
	res.first = 0;

	for (std::vector<uint64_t>::iterator i=map->file.begin(); i!=map->file.end(); ++i)
	{
		if ((nodes->getSize() >> 3) - wrote > 0)
		{
			*(uint64_t *)(&data[wrote<<3]) = *i;
			#ifdef DEBUG_ON
			printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[wrote<<3]));
			#endif
			wrote++;
		}
		else
		{
			res = nodes->write(res.first, data, true);
			*(uint64_t *)(&data[0]) = *i;
			#ifdef DEBUG_ON
			printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[0]));
			#endif
			wrote = 1;
		}
	}
	if ((nodes->getSize() >> 3) - wrote > 0)
	{
		*(uint64_t *)(&data[wrote<<3]) = 0;
		#ifdef DEBUG_ON
		printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[wrote<<3]));
		#endif
		wrote++;
	}
	else
	{
		res = nodes->write(res.first, data, true);
		*(uint64_t *)(&data[0]) = 0;
		#ifdef DEBUG_ON
		printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[0]));
		#endif
		wrote = 1;
	}

	for (std::vector<uint64_t>::iterator i=map->tag.begin(); i!=map->tag.end(); ++i)
	{
		if ((nodes->getSize() >> 3) - wrote > 0)
		{
			*(uint64_t *)(&data[wrote<<3]) = *i;
			#ifdef DEBUG_ON
			printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[wrote<<3]));
			#endif
			wrote++;
		}
		else
		{
			res = nodes->write(res.first, data, true);
			*(uint64_t *)(&data[0]) = *i;
			#ifdef DEBUG_ON
			printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[0]));
			#endif
			wrote = 1;
		}		
	}
	if ((nodes->getSize() >> 3) - wrote > 0)
	{
		*(uint64_t *)(&data[wrote<<3]) = 0;
		#ifdef DEBUG_ON
		printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[wrote<<3]));
		#endif
		res = nodes->write(res.first, data, false);
	}
	else
	{
		res = nodes->write(res.first, data, true);
		*(uint64_t *)(&data[0]) = 0;
		#ifdef DEBUG_ON
		printf("[DEBUG] wrote %llu\n", *(uint64_t *)(&data[0]));
		#endif
		res = nodes->write(res.first, data, false);	
	}
}

// ------------------------
void TagFS_Map::writeMaps()
// ------------------------
{
	if (!mapMap.file.empty())
	{
		write(mapMap.file[ mapMap.curFile ], &fileMap);
	}
	else
	{
		uint64_t tmp = nodes->reserve();
		#ifdef DEBUG_ON
		printf("[DEBUG] reserved %llu for filemap\n", tmp);
		#endif
		mapMap.file.push_back(tmp);
		write(tmp, &fileMap);
	}

	if (!mapMap.tag.empty())
	{
		write(mapMap.tag[ mapMap.curTag ], &tagMap);
	}
	else
	{
		uint64_t tmp = nodes->reserve();
		#ifdef DEBUG_ON
		printf("[DEBUG] reserved %llu for tagmap\n", tmp);
		#endif
		mapMap.tag.push_back(tmp);
		write(tmp, &tagMap);
	}
	write(&mapMap);
}


// ------------------------------------------------
uint8_t TagFS_Map::encodeInt(uint64_t i, char* out)
// ------------------------------------------------
{
	if (i <= 0xFF)
	{
		out[0] = 1;
		uint8_t oi = (uint8_t)i;
		out[1] = ((char*)(&oi))[0];
		return 2;
	}
	else if (i <= 0xFFFF)
	{
		out[0] = 2;
		uint16_t oi = (uint16_t)i;
		out[1] = ((char*)(&oi))[0];
		out[2] = ((char*)(&oi))[1];
		return 3;
	}
	else if (i <= 0xFFFFFFFF)
	{
		out[0] = 4;
		uint32_t oi = (uint32_t)i;
		out[1] = ((char*)(&oi))[0];
		out[2] = ((char*)(&oi))[1];
		out[3] = ((char*)(&oi))[2];
		out[4] = ((char*)(&oi))[3];
		return 5;
	}
	else
	{
		out[0] = 8;
		out[1] = ((char*)(&i))[0];
		out[2] = ((char*)(&i))[1];
		out[3] = ((char*)(&i))[2];
		out[4] = ((char*)(&i))[3];
		out[5] = ((char*)(&i))[4];
		out[6] = ((char*)(&i))[5];
		out[7] = ((char*)(&i))[6];
		out[8] = ((char*)(&i))[7];
		return 9;
	}
}

// -------------------------------------------------
uint64_t TagFS_Map::decodeInt(char* data, uint8_t i)
// -------------------------------------------------
{
	uint64_t result;
	if (i == 1)
	{
		result = *(uint8_t*)(data);
	}
	else if (i == 2)
	{
		result = *(uint16_t*)(data);
	}
	else if (i == 4)
	{
		result = *(uint32_t*)(data);
	}
	else if (i == 8)
	{
		result = *(uint64_t*)(data);
	}
	return result;
}

// ----------------------------------------------------------------------
void TagFS_Map::read(uint64_t nextNode, std::map<uint64_t, TagFile>* map)
// ----------------------------------------------------------------------
{
	uint16_t size;
	char data[nodes->getSize()];
	std::pair<uint64_t, TagFile> fileMeta;
	std::pair<std::string, uint64_t> reverseMeta;
	enum {
		MAP_FILE_ID,
		MAP_FILE_CTIME,
		MAP_FILE_MTIME,
		MAP_FILE_ATIME,
		MAP_FILE_SIZE,
		MAP_FILE_NODE,
		MAP_FILE_NAME,
		MAP_FILE_INIT
	} state = MAP_FILE_INIT;
	uint8_t needBytes = 0;
	uint8_t haveBytes = 0;
	char tmp[8];
	std::pair<uint64_t, int> res;
	res.first = nextNode;

	while ((res.first != 0 && state != MAP_FILE_INIT) || state == MAP_FILE_INIT)
	{
		if (state == MAP_FILE_INIT)
		{
			state = MAP_FILE_ID;
		}
		
		res = nodes->read(res.first, data);
		if (res.second != 0)
		{
			// error: I/O error
		}

		for (uint16_t i=0; i<nodes->getSize(); ++i)
		{
			if (data[i] == 0 && state == MAP_FILE_ID && needBytes == 0)
			{
				break;
			}
			else if (needBytes == 0 && state != MAP_FILE_NAME)
			{
				if (state == MAP_FILE_ID || state == MAP_FILE_SIZE || state == MAP_FILE_NODE)
				{
					needBytes = data[i];
				}
				else
				{
					needBytes = 4;
					--i;
				}
				haveBytes = 0;
			}
			else if (needBytes > 0)
			{
				if (haveBytes < needBytes)
				{
					tmp[haveBytes] = data[i];
					++haveBytes;
				}
				else
				{
					--i;
					if (state == MAP_FILE_ID)
					{
						fileMeta.first = decodeInt(tmp, needBytes);
						state = MAP_FILE_CTIME;
					}
					else if (state == MAP_FILE_CTIME)
					{
						fileMeta.second.ctime = decodeInt(tmp, needBytes);
						state = MAP_FILE_MTIME;
					}
					else if (state == MAP_FILE_MTIME)
					{
						fileMeta.second.mtime = decodeInt(tmp, needBytes);
						state = MAP_FILE_ATIME;
					}
					else if (state == MAP_FILE_ATIME)
					{
						fileMeta.second.atime = decodeInt(tmp, needBytes);
						state = MAP_FILE_SIZE;
					}
					else if (state == MAP_FILE_SIZE)
					{
						fileMeta.second.size = decodeInt(tmp, needBytes);
						state = MAP_FILE_NODE;
					}
					else if (state == MAP_FILE_NODE)
					{
						fileMeta.second.node = decodeInt(tmp, needBytes);
						state = MAP_FILE_NAME;
					}
					needBytes = 0;
				}
			}
			else if (state == MAP_FILE_NAME)
			{
				if (data[i] != 0)
				{
					fileMeta.second.name.append(&data[i], 1);
				}
				else
				{
					map->insert(fileMeta);
					reverseFileMap.insert(std::make_pair(fileMeta.second.name, fileMeta.first));
					fileMeta.second.name.clear();
					state = MAP_FILE_ID;
				}
			}
			else
			{
				// error: corrupted
			}
		}
	}
}

// -----------------------------------------------------------------------
void TagFS_Map::write(uint64_t thisNode, std::map<uint64_t, TagFile>* map)
// -----------------------------------------------------------------------
{
	char buf[nodes->getSize()];
	uint16_t bufpos = 0;
	std::pair<uint64_t, int> res;
	res.first = thisNode;

	for (std::map<uint64_t, TagFile>::iterator i=map->begin(); i!=map->end(); ++i)
	{
		res = writeData(i->first, 0, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.ctime, 4, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.mtime, 4, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.atime, 4, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.size, 0, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.node, 0, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		res = writeData(i->second.name, res.first, buf, &bufpos);
		if (res.second != 0)
		{
			break;
		}
		// write 0x00
		if (bufpos < nodes->getSize())
		{
			buf[bufpos] = 0;
			++bufpos;
		}
		else
		{
			res = nodes->write(res.first, buf, true);
			buf[0] = 0;
			bufpos = 1;
		}
	}
	// write ending 0x00
	if (bufpos < nodes->getSize())
	{
		buf[bufpos] = 0;
		res = nodes->write(res.first, buf, false);
	}
	else
	{
		res = nodes->write(res.first, buf, true);
		buf[0] = 0;
		res = nodes->write(res.first, buf, false);
	}
}

// --------------------
void TagFS_Map::print()
// --------------------
{
	std::map<uint64_t, TagFile>::iterator f;
	printf("[fileMap] start\n");
	for (f=fileMap.begin(); f!=fileMap.end(); ++f)
	{
		printf("[fileMap] %llu, %s, %u %u %u, %llu %llu\n",
			f->first,
			f->second.name.data(),
			f->second.ctime,
			f->second.mtime,
			f->second.atime,
			f->second.size,
			f->second.node);
	}
	printf("[fileMap] end\n");

	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator t;
	std::list<uint64_t>::iterator z;
	printf("[tagMap] start\n");
	for (t=tagMap.begin(); t!=tagMap.end(); ++t)
	{
		printf("[tagMap] %s =\n", t->first.data());
		for (z=t->second.second.begin(); z!=t->second.second.end(); ++z)
		{
			printf("%llu\n", *z);
		}
	}
	printf("[tagMap] end\n");
}

// -------------------------------
uint64_t TagFS_Map::getFileCount()
// -------------------------------
{
	return fileMap.size();
}

// --------------------------------------
uint64_t TagFS_Map::addFile(TagFile file)
// --------------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	uint64_t id = 1;
	for (i=fileMap.begin(); i!=fileMap.end(); ++i)
	{
		if (id != i->first)
		{
			break;
		}
		++id;
	}
	fileMap.insert(std::make_pair(id, file));
	reverseFileMap.insert(std::make_pair(file.name, id));
	fileMapSize += 38 + file.name.size();
	#ifdef DEBUG_ON
	printf("[DEBUG] fileMapSize = %u\n", fileMapSize);
	#endif
	return id;
}

// -----------------------------------------
uint64_t TagFS_Map::getFID(std::string name)
// -----------------------------------------
{
	std::map<std::string, uint64_t>::iterator rfmi;
	rfmi = reverseFileMap.find(name);
	if (rfmi == reverseFileMap.end())
	{
		return 0;
	}
	return rfmi->second;
}

// ---------------------------------------------------
void TagFS_Map::updateFile(uint64_t fid, TagFile info)
// ---------------------------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	i = fileMap.find(fid);
	
	if (i != fileMap.end())
	{
		if (i->second.name != info.name)
		{
			fileMapSize += info.name.size() - i->second.name.size();
			std::map<std::string, uint64_t>::iterator ri;
			ri = reverseFileMap.find(i->second.name);
			reverseFileMap.erase(ri);
			reverseFileMap.insert(std::make_pair(info.name, i->first));
		}
		i->second = info;
	}
	
	#ifdef DEBUG_ON
	printf("[DEBUG] fileMapSize = %u\n", fileMapSize);
	#endif
}

// -------------------------------------
void TagFS_Map::updateFile(std::string oldname, TagFile info)
// -------------------------------------
{
	std::map<std::string, uint64_t>::iterator ri;
	ri = reverseFileMap.find(oldname);
	if (ri != reverseFileMap.end())
	{
		updateFile(ri->second, info);
	}
}

// -------------------------------------
TagFile TagFS_Map::getFile(uint64_t fid)
// -------------------------------------
{
	std::map<uint64_t, TagFile>::iterator fmi;
	TagFile file;
	file.node = 0;
	fmi = fileMap.find(fid);
	if (fmi != fileMap.end())
	{
		file = fmi->second;
	}
	return file;
}

// -----------------------------------------
TagFile TagFS_Map::getFile(std::string name)
// -----------------------------------------
{
	std::map<uint64_t, TagFile>::iterator fmi;
	std::map<std::string, uint64_t>::iterator rfmi;
	TagFile file;
	file.node = 0;

	rfmi = reverseFileMap.find(name);
	if (rfmi == reverseFileMap.end())
	{
		return file;
	}

	fmi = fileMap.find(rfmi->second);
	if (fmi != fileMap.end())
	{
		file = fmi->second;
	}
	return file;
}

// ----------------------------------
void TagFS_Map::rmvFile(uint64_t fid)
// ----------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	std::map<std::string, uint64_t>::iterator ri;
	i = fileMap.find(fid);
	ri = reverseFileMap.find(i->second.name);
	fileMapSize -= i->second.name.size() + 38;
	fileMap.erase(i);
	reverseFileMap.erase(ri);
	#ifdef DEBUG_ON
	printf("[DEBUG] fileMapSize = %u\n", fileMapSize);
	#endif
}

// --------------------------------------
void TagFS_Map::rmvFile(std::string name)
// --------------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	std::map<std::string, uint64_t>::iterator ri;
	ri = reverseFileMap.find(name);
	i = fileMap.find(ri->second);
	fileMapSize -= i->second.name.size() + 38;
	fileMap.erase(i);
	reverseFileMap.erase(ri);
	#ifdef DEBUG_ON
	printf("[DEBUG] fileMapSize = %u\n", fileMapSize);
	#endif
}

// --------------------------------------------------
void TagFS_Map::addTag(std::string tag, uint64_t fid)
// --------------------------------------------------
{
	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator tmi;
	tmi = tagMap.find(tag);
	if (tmi != tagMap.end())
	{
		tmi->second.second.push_back(fid);
		tmi->second.second.sort();
		tmi->second.second.unique();
		tagMapSize += 8;
	}
	else
	{
		std::pair<uint64_t, std::list<uint64_t> > nwtag;
		nwtag.second.push_back(fid);
		tagMap.insert(std::make_pair(tag, nwtag));
		tagMapSize += tag.size() + 16;
	}
	#ifdef DEBUG_ON
	printf("[DEBUG] tagMapSize = %u\n", tagMapSize);
	#endif
}

// ------------------------------------------------------
void TagFS_Map::addTag(std::string tag, std::string name)
// ------------------------------------------------------
{
	std::map<std::string, uint64_t>::iterator ri;
	ri = reverseFileMap.find(name);
	if (ri != reverseFileMap.end())
	{
		addTag(tag, ri->second);
	}
}

// --------------------------------------------------
void TagFS_Map::rmvTag(std::string tag, uint64_t fid)
// --------------------------------------------------
{
	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator tmi;
	tmi = tagMap.find(tag);
	if (tmi != tagMap.end())
	{
		tmi->second.second.remove(fid);
		tagMapSize -= 8;
		if (tmi->second.second.empty())
		{
			tagMapSize -= tmi->first.size() + 8;
			tagMap.erase(tmi);
		}
	}
	#ifdef DEBUG_ON
	printf("[DEBUG] tagMapSize = %u\n", tagMapSize);
	#endif
}

// ------------------------------------------------------
void TagFS_Map::rmvTag(std::string tag, std::string name)
// ------------------------------------------------------
{
	std::map<std::string, uint64_t>::iterator ri;
	ri = reverseFileMap.find(name);
	if (ri != reverseFileMap.end())
	{
		rmvTag(tag, ri->second);
	}
}

// ----------------------------------------------------
std::list<std::string> TagFS_Map::getTags(uint64_t fid)
// ----------------------------------------------------
{
	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator tmi;
	std::list<uint64_t>::iterator li;
	std::list<std::string> tags;

	for (tmi=tagMap.begin(); tmi!=tagMap.end(); ++tmi)
	{
		for (li=tmi->second.second.begin(); li!=tmi->second.second.end(); ++li)
		{
			if (*li == fid)
			{
				tags.push_back(tmi->first);
			}
		}
	}
	return tags;
}

// --------------------------------------------------------
std::list<std::string> TagFS_Map::getTags(std::string name)
// --------------------------------------------------------
{
	std::list<std::string> list;
	std::map<std::string, uint64_t>::iterator ri;
	ri = reverseFileMap.find(name);
	if (ri != reverseFileMap.end())
	{
		list = getTags(ri->second);
	}
	return list;
}

// -------------------------------------
std::list<uint64_t> TagFS_Map::tagFIDs()
// -------------------------------------
{
	std::list<uint64_t> list;
	std::map<uint64_t, TagFile>::iterator fmi;
	for (fmi=fileMap.begin(); fmi!=fileMap.end(); ++fmi)
	{
		list.push_back(fmi->first);
	}
	return list;
}

// ----------------------------------------------------
std::list<uint64_t> TagFS_Map::tagFIDs(std::string tag)
// ----------------------------------------------------
{
	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator tmi;
	std::list<uint64_t> list;
	tmi = tagMap.find(tag);
	if (tmi != tagMap.end())
	{
		list = tmi->second.second;
		tmi->second.first += 1;
	}
	return list;
}

// ------------------------------------------------------------------------------------------------------------
void TagFS_Map::read(uint64_t nextNode, std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >* map)
// ------------------------------------------------------------------------------------------------------------
{
	uint16_t size;
	char data[nodes->getSize()];
	std::pair<std::string, std::pair<uint64_t, std::list<uint64_t> > > tagdata;
	enum {
		MAP_TAG_COUNT,
		MAP_TAG_NAME,
		MAP_TAG_ID,
		MAP_TAG_INIT
	} state = MAP_TAG_INIT;
	uint8_t needBytes = 0;
	uint8_t haveBytes = 0;
	char tmp[8];
	std::pair<uint64_t, int> res;
	res.first = nextNode;
	uint64_t tmpid;

	while ((res.first != 0 && state != MAP_TAG_INIT) || state == MAP_TAG_INIT)
	{
		if (state == MAP_TAG_INIT)
		{
			state = MAP_TAG_COUNT;
		}
		
		res = nodes->read(res.first, data);
		if (res.second != 0)
		{
			// error: I/O error
		}

		for (uint16_t i=0; i<nodes->getSize(); ++i)
		{
			if (data[i] == 0 && state == MAP_TAG_COUNT && needBytes == 0)
			{
				res.first = 0;
				break;
			}
			else if (data[i] == 0 && state == MAP_TAG_ID && needBytes == 0)
			{
				map->insert(tagdata);
				tagdata.first.clear();
				tagdata.second.second.clear();
				state = MAP_TAG_COUNT;
			}
			else if (needBytes == 0 && state != MAP_TAG_NAME)
			{
				if (state == MAP_TAG_COUNT || state == MAP_TAG_ID)
				{
					needBytes = data[i];
				}
				haveBytes = 0;
			}
			else if (needBytes > 0)
			{
				if (haveBytes < needBytes)
				{
					tmp[haveBytes] = data[i];
					++haveBytes;
				}
				else
				{
					--i;
					if (state == MAP_TAG_COUNT)
					{
						tagdata.second.first = decodeInt(tmp, needBytes);
						state = MAP_TAG_NAME;
					}
					else if (state == MAP_TAG_ID)
					{
						tmpid = decodeInt(tmp, needBytes);
						tagdata.second.second.push_back(tmpid);
					}
					needBytes = 0;
				}
			}
			else if (state == MAP_TAG_NAME)
			{
				if (data[i] != 0)
				{
					tagdata.first.append(&data[i], 1);
				}
				else
				{
					state = MAP_TAG_ID;
				}
			}
			else
			{
				// error: corrupted
			}
		}
	}
}

// -------------------------------------------------------------------------------------------------------------
void TagFS_Map::write(uint64_t nextNode, std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >* map)
// -------------------------------------------------------------------------------------------------------------
{
	std::list<uint64_t>::iterator idi;
	char buf[nodes->getSize()];
	uint16_t bufpos = 0;
	uint16_t bufmax = nodes->getSize();
	char intbuf[9];
	uint8_t intbytes = 0;
	uint16_t tmp = 0;
	std::string tmpstr;
	std::pair<uint64_t, int> res;
	res.first = nextNode;

	for (std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator i=map->begin(); i!=map->end(); ++i)
	{
		// write count
		intbytes = encodeInt(i->second.first, intbuf);
		if (bufpos+intbytes <= bufmax)
		{
			tools::copyChars((const char*)intbuf, &buf[bufpos], intbytes);
			bufpos += intbytes;
		}
		else
		{
			tools::copyChars((const char*)intbuf, &buf[bufpos], intbytes-(bufpos+intbytes-bufmax));
			res = nodes->write(res.first, buf, true);
			tools::copyChars((const char*)(&((char*)(&intbuf))[intbytes-(bufpos+intbytes-bufmax)]), buf, intbytes-(intbytes-(bufpos+intbytes-bufmax)));
			bufpos = intbytes-(intbytes-(bufpos+intbytes-bufmax));
		}
		// write name
		tmpstr = i->first;
		while (!tmpstr.empty())
		{
			if (bufpos+tmpstr.size() <= bufmax)
			{
				tools::copyChars(tmpstr.data(), &buf[bufpos], tmpstr.size());
				bufpos += tmpstr.size();
				tmpstr.clear();
			}
			else
			{
				tools::copyChars(tmpstr.data(), &buf[bufpos], tmpstr.size()-(bufpos+intbytes-bufmax));
				res = nodes->write(res.first, buf, true);
				tmpstr = tmpstr.substr(tmpstr.size()-(bufpos+intbytes-bufmax));
				bufpos = 0;
			}
		}
		buf[bufpos] = 0;
		++bufpos;

		// write ids
		for (idi=i->second.second.begin(); idi!=i->second.second.end(); ++idi)
		{
			intbytes = encodeInt(*idi, intbuf);
			if (bufpos+intbytes <= bufmax)
			{
				tools::copyChars((const char*)intbuf, &buf[bufpos], intbytes);
				bufpos += intbytes;
			}
			else
			{
				tools::copyChars((const char*)intbuf, &buf[bufpos], intbytes-(bufpos+intbytes-bufmax));
				res = nodes->write(res.first, buf, true);
				tools::copyChars((const char*)(&((char*)(&intbuf))[intbytes-(bufpos+intbytes-bufmax)]), buf, intbytes-(intbytes-(bufpos+intbytes-bufmax)));
				bufpos = intbytes-(intbytes-(bufpos+intbytes-bufmax));
			}
		}
		// write ending 0x00
		if (bufpos < bufmax)
		{
			buf[bufpos] = 0;
			++bufpos;
		}
		else
		{
			res = nodes->write(res.first, buf, true);
			buf[0] = 0;
			bufpos = 1;
		}	
	}
	// write ending 0x00
	if (bufpos < bufmax)
	{
		buf[bufpos] = 0;
		res = nodes->write(res.first, buf, false);
	}
	else
	{
		res = nodes->write(res.first, buf, true);
		buf[0] = 0;
		res = nodes->write(res.first, buf, false);
	}	
}

// --------------------------------------------------------------------------------------------------------------------------
std::pair<uint64_t, int> TagFS_Map::writeData(uint64_t integer, uint8_t intbytes, uint64_t node, char* buf, uint16_t* bufpos)
// --------------------------------------------------------------------------------------------------------------------------
{
	std::pair<uint64_t, int> res;
	res.first = node;
	res.second = 0;

	char* intbuf;
	char intbufc[9];
	if (intbytes == 0)
	{
		intbytes = encodeInt(integer, intbufc);
		intbuf = intbufc;
	}
	else
	{
		intbuf = (char*)(&integer);
	}

	uint16_t bufmax = nodes->getSize();
	if (*bufpos+intbytes < bufmax)
	{
		tools::copyChars((const char*)intbuf, &buf[*bufpos], intbytes);
		*bufpos += intbytes;
	}
	else
	{
		// fill how much fits at the end of the buf and write
		tools::copyChars((const char*)intbuf, &buf[*bufpos], intbytes-(*bufpos+intbytes-bufmax));
		res = nodes->write(res.first, buf, true);
		if (res.second != 0)
		{
			return res;
		}

		uint8_t bytesleft = intbytes-(intbytes-(*bufpos+intbytes-bufmax));

		while (bytesleft > 0)
		{
			if (bytesleft >= bufmax)
			{
				tools::copyChars((const char*)(&intbuf[intbytes-bytesleft]), buf, bufmax);
				res = nodes->write(res.first, buf, true);
				if (res.second != 0)
				{
					return res;
				}
				bytesleft -= bufmax;
				*bufpos = 0;
			}
			else
			{
				tools::copyChars((const char*)(&intbuf[intbytes-bytesleft]), buf, bytesleft);
				*bufpos = bytesleft;
				bytesleft = 0;
			}
		}
	}
	return res;
}

// ----------------------------------------------------------------------------------------------------------
std::pair<uint64_t, int> TagFS_Map::writeData(std::string string, uint64_t node, char* buf, uint16_t* bufpos)
// ----------------------------------------------------------------------------------------------------------
{
	std::pair<uint64_t, int> res;
	res.first = node;
	res.second = 0;
	uint16_t bufmax = nodes->getSize();

	uint64_t strsize = string.size();

	if (*bufpos+strsize < bufmax)
	{
		tools::copyChars(string.data(), &buf[*bufpos], strsize);
		*bufpos += strsize;
	}
	else
	{
		// fill how much fits at the end of the buf and write
		tools::copyChars(string.data(), &buf[*bufpos], strsize-(*bufpos+strsize-bufmax));
		res = nodes->write(res.first, buf, true);
		if (res.second != 0)
		{
			return res;
		}

		uint64_t bytesleft = strsize-(strsize-(*bufpos+strsize-bufmax));

		while (bytesleft > 0)
		{
			if (bytesleft >= bufmax)
			{
				tools::copyChars(&(string.data()[strsize-bytesleft]), buf, bufmax);
				res = nodes->write(res.first, buf, true);
				if (res.second != 0)
				{
					return res;
				}
				bytesleft -= bufmax;
				*bufpos = 0;
			}
			else
			{
				tools::copyChars(&(string.data()[strsize-bytesleft]), buf, bytesleft);
				*bufpos = bytesleft;
				bytesleft = 0;
			}
		}
	}
	if (*bufpos < bufmax)
	{
		buf[*bufpos] = 0;
	}
	else
	{
		res = nodes->write(res.first, buf, true);
		buf[0] = 0;
		*bufpos = 0;
	}
	return res;
}

// ------------------------
uint8_t calcInt(uint64_t i)
// ------------------------
{
	if (i <= 0xFF) return 2;
	if (i <= 0xFFFF) return 3;
	if (i <= 0xFFFFFFFF) return 5;
	return 9;
}

// -------------------------------------------------------------------------------------
uint32_t calcTag(std::pair<std::string, std::pair<uint64_t, std::list<uint64_t> > >* map)
// -------------------------------------------------------------------------------------
{
	uint32_t total = 0;

	total += map->first.size() + 1;
	total += calcInt(map->second.first);

	std::list<uint64_t>::iterator z;
	for (z=map->second.second.begin(); z!=map->second.second.end(); ++z)
	{
		total += calcInt(*z);
	}

	return total;
}

// -----------------------------------------------------------------------------------------------------
void TagFS_Map::reserveFileNodes(std::map<uint64_t, TagFile>* map)
// -----------------------------------------------------------------------------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	std::list<uint64_t> nods;
	std::list<uint64_t>::iterator ni;
	for (i=map->begin(); i!=map->end(); ++i)
	{
		nods = nodes->getList(i->second.node);
		for (ni=nods.begin(); ni!=nods.end(); ++ni)
		{
			nodes->reserve(*ni);
		}
	}
}

// -----------------------------------------------------------------------------------------------------
uint32_t TagFS_Map::fileMapSizeCalc(std::map<uint64_t, TagFile> *map)
// -----------------------------------------------------------------------------------------------------
{
	std::map<uint64_t, TagFile>::iterator i;
	uint32_t size = 0;
	for (i=map->begin(); i!=map->end(); ++i)
	{
		size += 38 + i->second.name.size();
	}
	return size;
}

// -----------------------------------------------------------------------------------------------------
uint32_t TagFS_Map::tagMapSizeCalc(std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > > *map)
// -----------------------------------------------------------------------------------------------------
{
	uint32_t size = 0;
	std::map<std::string, std::pair<uint64_t, std::list<uint64_t> > >::iterator i;
	for (i=map->begin(); i!=map->end(); ++i)
	{
		size += i->first.size() + 8 + (i->second.second.size() << 3);
	}
	return size;
}

